
#pragma once

#include "SimpleC.hpp"

#include "asio.hpp"

class SimpleSerial : public SimpleC
{
public:
    SimpleSerial(const char* name, int baudrate = 115200, char parity = 'N', int bits = 8, int stopbits = 1)
    {
        LOG_PRINT("open seri {}. ({}-{}-{}-{})", name, baudrate, parity, bits, stopbits);

        io_pipe_.open(name);
        io_pipe_.set_option(asio::serial_port_base::baud_rate(baudrate));
        io_pipe_.set_option(asio::serial_port_base::character_size(bits));
        switch (parity)
        {
        case 'E':
            io_pipe_.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::even));
            break;
        case 'O':
            io_pipe_.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::odd));
            break;
        case 'N':
            io_pipe_.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::none));
            break;
        default:
            throw std::runtime_error("Invalid parity setting");
        }
        switch (stopbits)
        {
        case 1:
            io_pipe_.set_option(asio::serial_port_base::stop_bits(asio::serial_port_base::stop_bits::one));
            break;
        case 2:
            io_pipe_.set_option(asio::serial_port_base::stop_bits(asio::serial_port_base::stop_bits::two));
            break;
        default:
            throw std::runtime_error("Invalid stop bit setting");
        }
        io_pipe_.set_option(asio::serial_port_base::flow_control(asio::serial_port_base::flow_control::none));

        start();
    }

    int write(const nonstd::span<char>& arr) override
    {
        asio::error_code ec;

        int len = (int)io_pipe_.write_some(asio::buffer(arr.data(), arr.size()), ec);

        if (ec)
        {
            LOGE_PRINT("write error: {} {}", ec.value(), ec.message());
            return -1;
        }

        return len;
    }

protected:
    void close() override
    {
        io_pipe_.close();
    }

    int read(const nonstd::span<char>& arr, unsigned int timeout_ms) override
    {
        asio::error_code ec;

        int len = (int)io_pipe_.read_some(asio::buffer(arr.data(), arr.size()), ec);
        if (ec)
        {
            LOGE_PRINT("read error: {} {}", ec.value(), ec.message());
            return -1;
        }
        return len;
    }

private:
    asio::io_service  io_ctx_;
    asio::serial_port io_pipe_{io_ctx_};
};
